package com.qinglei.recoup.system.service.impl;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qinglei.recoup.common.domain.CommonRes;
import com.qinglei.recoup.common.exception.RecoupException;
import com.qinglei.recoup.common.exception.RedisConnectException;
import com.qinglei.recoup.common.tenant.TenantContextHolder;
import com.qinglei.recoup.common.utils.RecoupUtil;
import com.qinglei.recoup.common.utils.DateTimeUtils;
import com.qinglei.recoup.system.dao.BedMonitorTimeMapper;
import com.qinglei.recoup.system.domain.BedMonitorTime;
import com.qinglei.recoup.system.enums.EventEnum;
import com.qinglei.recoup.system.pojo.BedMonitorTimeVO;
import com.qinglei.recoup.system.pojo.LocalTimeVO;
import com.qinglei.recoup.system.pojo.BedMonitorTimeParam;
import com.qinglei.recoup.system.pojo.TimePeriodParam;
import com.qinglei.recoup.system.pojo.MonitorTimeUpdateParam;
import com.qinglei.recoup.system.service.BedMonitorTimeService;
import com.qinglei.recoup.system.service.BedService;
import com.qinglei.recoup.system.service.EventLogService;
import org.springframework.beans.BeanUtils;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;

/**
 * @author syp
 */
@Service
public class BedMonitorTimeServiceImpl extends ServiceImpl<BedMonitorTimeMapper, BedMonitorTime> implements BedMonitorTimeService {

    @Resource
    @Lazy
    private EventLogService eventLogService;

    @Resource
    @Lazy
    private BedService bedService;

    /**
     * 添加睡眠监控时间设置
     *
     * @param bedMonitorTimeParam
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Integer addBedMonitorTime(BedMonitorTimeParam bedMonitorTimeParam) throws Exception {
        boolean checkRes = this.checkTimeOverlap(bedMonitorTimeParam.getStartTime(), bedMonitorTimeParam.getEndTime(),
                bedMonitorTimeParam.getBedId(), null);
        if (checkRes) {
            throw new RecoupException("时间有冲突，请重新选择时间");
        }
        BedMonitorTime bedMonitorTime = new BedMonitorTime();
        BeanUtils.copyProperties(bedMonitorTimeParam, bedMonitorTime);
        int count = baseMapper.insert(bedMonitorTime);
        if (count > 0) {
            // 记录事件日志并推送消息
            eventLogService.procEventLog(bedMonitorTime.getBedId(), RecoupUtil.getCurrentUser().getId(),
                    null, EventEnum.MONITOR_TIME_ADD.getCode(), null, TenantContextHolder.getTenantId());
            // 添加监控时间段到缓存
            bedService.addBetMonitorTime(bedMonitorTime);
            // 推送床位当前状态
            bedService.pushBedCurStatus(bedMonitorTime.getBedId());
            return bedMonitorTime.getId();
        }
        throw new RecoupException("添加睡眠监控时间设置失败");
    }

    /**
     * 检查时间是否重叠
     *
     * @param startTimeStr
     * @param endTimeStr
     * @param bedId
     * @param excludeId
     * @return true - 有重叠 false - 无重叠
     */
    private boolean checkTimeOverlap(String startTimeStr, String endTimeStr, Integer bedId, Integer excludeId) {
        //传入时间段
        TimePeriodParam timePeriodIn = new TimePeriodParam(startTimeStr, endTimeStr);

        // 查询床位监测时间段
        List<BedMonitorTime> bedMonitorTimes = baseMapper.selectList(Wrappers.<BedMonitorTime>lambdaQuery()
                .eq(BedMonitorTime::getBedId, bedId)
                .ne(excludeId != null, BedMonitorTime::getId, excludeId));

        // 遍历比较时间是否有重叠
        for (BedMonitorTime bmt : bedMonitorTimes) {
            TimePeriodParam timePeriodCur = new TimePeriodParam(bmt.getStartTime(), bmt.getEndTime());
            boolean res = DateTimeUtils.checkTimeOverlap(timePeriodIn, timePeriodCur);
            if (res) {
                return true;
            }
        }
        return false;
    }

    /**
     * 修改睡眠监控时间设置
     *
     * @param param
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public BedMonitorTime updateBedMonitorTime(MonitorTimeUpdateParam param) throws Exception {
        BedMonitorTime bedMonitorTimeOld = baseMapper.selectById(param.getId());
        if (bedMonitorTimeOld == null) {
            throw new RecoupException("记录不存在");
        }

        boolean checkRes = this.checkTimeOverlap(param.getStartTime(), param.getEndTime(),
                bedMonitorTimeOld.getBedId(), param.getId());
        if (checkRes) {
            throw new RecoupException("时间有冲突，请重新选择时间");
        }
        BedMonitorTime bedMonitorTimeNew = new BedMonitorTime();
        BeanUtils.copyProperties(bedMonitorTimeOld, bedMonitorTimeNew);
        bedMonitorTimeNew.setStartTime(param.getStartTime());
        bedMonitorTimeNew.setEndTime(param.getEndTime());
        int count = baseMapper.updateById(bedMonitorTimeNew);
        if (count > 0) {
            // 更新监控时间段到缓存
            bedService.updateBetMonitorTime(bedMonitorTimeOld, bedMonitorTimeNew);
            // 推送床位当前状态
            bedService.pushBedCurStatus(bedMonitorTimeOld.getBedId());
            return bedMonitorTimeNew;
        }
        throw new RecoupException("修改睡眠监控时间设置失败");
    }

    /**
     * 同步床位所有监测时间段
     *
     * @param monitorTimes
     * @param bedId
     * @throws RedisConnectException
     */
    @Override
    public void syncMonitorTimes(List<TimePeriodParam> monitorTimes, Integer bedId) throws RedisConnectException {
        List<BedMonitorTime> bedMonitorTimes = baseMapper.selectList(Wrappers.<BedMonitorTime>lambdaQuery().eq(BedMonitorTime::getBedId, bedId));
        for (BedMonitorTime bedMonitorTime : bedMonitorTimes) {
            // 删除监控时间段缓存
            bedService.delBetMonitorTime(bedMonitorTime);
            baseMapper.deleteById(bedMonitorTime.getId());
        }
        for (TimePeriodParam monitorTime : monitorTimes) {
            BedMonitorTime bedMonitorTime = new BedMonitorTime();
            bedMonitorTime.setBedId(bedId);
            bedMonitorTime.setStartTime(monitorTime.getStartTime());
            bedMonitorTime.setEndTime(monitorTime.getEndTime());
            baseMapper.insert(bedMonitorTime);
            // 添加监控时间段到缓存
            bedService.addBetMonitorTime(bedMonitorTime);
        }
        // 推送床位当前状态
        bedService.pushBedCurStatus(bedId);
    }

    /**
     * 删除睡眠监控时间设置
     *
     * @param id
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public BedMonitorTime delBedMonitorTime(Integer id) throws Exception {
        BedMonitorTime bedMonitorTime = baseMapper.selectById(id);
        if (bedMonitorTime == null) {
            throw new RecoupException("记录不存在");
        }
        Integer bmtCount = baseMapper.selectCount(Wrappers.<BedMonitorTime>lambdaQuery().eq(BedMonitorTime::getBedId, bedMonitorTime.getBedId()));
        if (bmtCount <2){
            throw new RecoupException("监控时间不可全部删除");
        }
        int count = baseMapper.deleteById(id);
        if (count > 0) {
            // 记录事件日志并推送消息
            eventLogService.procEventLog(bedMonitorTime.getBedId(), RecoupUtil.getCurrentUser().getId(),
                    null, EventEnum.MONITOR_TIME_DELETE.getCode(), null, TenantContextHolder.getTenantId());
            // 删除监控时间段缓存
            bedService.delBetMonitorTime(bedMonitorTime);
            // 推送床位当前状态
            bedService.pushBedCurStatus(bedMonitorTime.getBedId());
            return bedMonitorTime;
        }
        throw new RecoupException("删除睡眠监控时间设置失败");
    }

    /**
     * 获取指定床位监测时间段列表
     *
     * @param bedId
     * @return
     */
    @Override
    public List<BedMonitorTimeVO> getBedMonitorTimeList(Integer bedId) {
        List<BedMonitorTimeVO> bedMonitorTimeVOS = new ArrayList<>();
        List<BedMonitorTime> bedMonitorTimes = baseMapper.selectList(
                Wrappers.<BedMonitorTime>lambdaQuery().eq(BedMonitorTime::getBedId, bedId)
                        .orderByAsc(BedMonitorTime::getStartTime));
        for (BedMonitorTime bmt : bedMonitorTimes) {
            BedMonitorTimeVO bedMonitorTimeVO = new BedMonitorTimeVO();
            BeanUtils.copyProperties(bmt, bedMonitorTimeVO);
            bedMonitorTimeVOS.add(bedMonitorTimeVO);
        }
        return bedMonitorTimeVOS;
    }

    /**
     * 获取下一个监测时间段
     *
     * @param bedId
     * @return
     */
    @Override
    public BedMonitorTimeVO getNextMonitorTime(Integer bedId) {
        String nowTime = LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm"));
        BedMonitorTime bedMonitorTime = this.baseMapper.selectOne(Wrappers.<BedMonitorTime>lambdaQuery().eq(BedMonitorTime::getBedId, bedId)
                .ge(BedMonitorTime::getStartTime, nowTime)
                .orderByAsc(BedMonitorTime::getStartTime)
                .last("limit 1"));
        if (bedMonitorTime == null) {
            bedMonitorTime = this.baseMapper.selectOne(Wrappers.<BedMonitorTime>lambdaQuery().eq(BedMonitorTime::getBedId, bedId)
                    .orderByAsc(BedMonitorTime::getStartTime)
                    .last("limit 1"));

        }
        if (bedMonitorTime == null) {
            return null;
        }
        BedMonitorTimeVO bedMonitorTimeVO = new BedMonitorTimeVO();
        BeanUtils.copyProperties(bedMonitorTime, bedMonitorTimeVO);
        return bedMonitorTimeVO;
    }

}
